/**
 * @name Context menu
 * @description Responsible for provide context menu functionality
 */

jt.module('ContextMenu', function(context) {
	
	// Useful shorthands
	var canvas = context.canvas,
		settings = context.settings,
		gh = context.settings.grid.h,
		gv = context.settings.grid.v,
		document = context.document,
		fn = context.fn,
		TWEEN = context.require('TWEEN'),

		menusRef = [];
	
	context.on('init', function() {
		context.log('[ContextMenu] init');
	});

	// Start tweening interval
//	TWEEN.start();

	/**
	 * Context menu class used by jtop
	 * @class Standard context menu class
	 * @param {object} options All context menu options
	 */
	var ContextMenu = function(options) {

		if(!(this instanceof ContextMenu)) {
			var menu =  new ContextMenu(options);
			menusRef.push(menu)
			return menu;
		}

		var o = this._options = util.extend({
			width: 130,
			defaultSide: 'down',
			showArrow: true,
			offsetTop: 50,
			offsetBottom: 38, 
			iconWidth: 16,
			iconHeight: 16,
			elements: {},
			delay: 0
		}, options),

		body = document.getElementsByTagName('body')[0],
		htmlMenuList = document.createElement('ul'),
		htmlMenuElement = document.createElement('li'),
		htmlMenuDiv = document.createElement('div'),
		htmlMenuImg = document.createElement('div'),
		htmlMenuTitle = document.createElement('a'),
		
		htmlArrow = document.createElement('div'),
		htmlArrowBorder = document.createElement('div');

		htmlMenuDiv.appendChild(htmlMenuImg);
		htmlMenuDiv.appendChild(htmlMenuTitle);
		htmlMenuElement.appendChild(htmlMenuDiv);
		
		htmlMenuList.appendChild(htmlArrowBorder);
		htmlMenuList.appendChild(htmlArrow);
		
		body.appendChild(htmlMenuList);

		util.addClass(htmlMenuList, 'jtContextMenu');
		util.addClass(htmlArrow, 'jtContextMenuArrow');
		util.addClass(htmlArrowBorder, 'jtContextMenuArrowBorder');
		util.addClass(htmlMenuImg, 'img');

		this._tweenI = new TWEEN.Tween(this._style);
		this._tweenO = new TWEEN.Tween(this._style);

		this._getElementTemplate = function() {
			return htmlMenuElement.cloneNode(true);
		}

		this._getMenuHtml = function() {
			return htmlMenuList;
		}

		this._getArrow = function() {
			return htmlArrow;
		}

		this._getArrowBorder = function() {
			return htmlArrowBorder;
		}
	};

	/**
	 * Context menu prototype
	 * @type {ContextMenu}
	 */
	ContextMenu.prototype = {

		_sender: null,

		_style: {
			opacity: 0
		},

		/**
		 * Adds element position to menu
		 * @param {string} title Title of element (it will be shown in the menu position).
		 * @param {string} icon Path to the element icon (optional).
		 * @param {function} handler Handler function for element click event..
		 */
		addMenuElement: function(title, icon, handler) {
			var menu = this._getMenuHtml(),
				el = this._getElementTemplate(),
				div = el.getElementsByTagName('div')[0],
				text = div.getElementsByTagName('a')[0],
				img = div.getElementsByClassName('img')[0];

			var self = this;
			
			text.href = '#';
			text.innerHTML = title;

			if(util.is(handler, 'function')) {
				util.addEvent(el, 'click', function(e) {
					handler.call(self, self._sender);
					e.preventDefault();
					return false;
				});
			}

			if(util.is(icon, 'string')) {
				img.style.backgroundImage = 'url(' + icon + ')';
			}

			menu.appendChild(el);

			return this;
		},

		/**
		 * Adds separator element to menu
		 */
		addMenuSeparator: function() {
			return this;
		},

		/**
		 * Helper function to calculate viewport offset of element
		 * @param  {HTMLElement} el Element to calculate offset
		 * @return {offset}
		 */
		_getViewportOffset: function(el) {
		
			var top = el.offsetTop;
			var left = el.offsetLeft;
			var width = el.offsetWidth;
			var height = el.offsetHeight;

			while(el.offsetParent) {
				el = el.offsetParent;
				top += el.offsetTop;
				left += el.offsetLeft;
			}

			return {
				top: top - window.pageYOffset,
				left: left - window.pageXOffset,
				bottom: (window.pageYOffset + window.innerHeight) - (top + height),
				right: (window.pageXOffset + window.innerWidth) - (left + width) 
			};
		},

		/**
		 * Shows menu at given position
		 * @param  {integer} x X position.
		 * @param  {integer} y Y position
		 * @return {null}
		 */
		show: function(x, y) {
			var menu = this._getMenuHtml(),
				that = this,
				arrow = this._getArrow(),
				arrowBorder = this._getArrowBorder(),
				o = this._options,
				posX, 
				posY, 
				arrowX, 
				arrowY,
				canUpPos;
			
			// Display first to calculate bbox
			util.extend(menu.style, {
				display: 'block',
				opacity: '0',
				top: y + 'px',
				left: x + 'px'
			});

			// Calculate menu position
			var width = menu.offsetWidth,
				height = menu.offsetHeight;
			
			posX = x - width / 2;
		
			if(posX - window.pageXOffset < 0) {
				posX += (window.pageXOffset - posX) + 10;
			} else if ((window.pageXOffset + window.innerWidth) - (posX + width) < 0) {
				posX -= (posX + width) - (window.pageXOffset + window.innerWidth) + 25;
			}

			arrowX = x - posX - 7;

			canUpPos = (((o.defaultSide === 'up') && (y - height - o.offsetBottom - window.pageYOffset) > 0) 
					|| ((o.defaultSide === 'down') && (window.pageYOffset + window.innerHeight) - (y + height + o.offsetTop) < 0) )

			menu.style.left = posX + 'px';
			menu.style.top = canUpPos
							 ? y - height - o.offsetBottom + 'px' 
							 : y + o.offsetTop + 'px';

			// calculate arrow position
			if(canUpPos) {	// above
				
				util.removeClass(arrow, 'jtArrowDown');
				util.addClass(arrow, 'jtArrowUp');
				
				util.extend(arrow.style, {
					'left': arrowX + 'px',
				});

				util.removeClass(arrowBorder, 'jtArrowBorderDown');
				util.addClass(arrowBorder, 'jtArrowBorderUp');

				util.extend(arrowBorder.style, {
					'left': arrowX + 'px',
				});
			} else {															// below
				
				util.removeClass(arrow, 'jtArrowUp');
				util.addClass(arrow, 'jtArrowDown');

				util.extend(arrow.style, {
					'left': arrowX + 'px',
				});

				util.removeClass(arrowBorder, 'jtArrowBorderUp');
				util.addClass(arrowBorder, 'jtArrowBorderDown');

				util.extend(arrowBorder.style, {
					'left': arrowX + 'px',
				});
			}

			// hide all other menus
			for(var i = 0, len = menusRef.length; i < len; i++) if(menusRef[i] !== this) {
				menusRef[i].hide();
			}

			// Tween menu show
    		that._tweenI.stop()
     		.to({'opacity': '1'}, 100)
     		.onUpdate(function() {
     			menu.style.opacity = that._style.opacity;
     		})
     		.onComplete(function() {
     			menu.style.display = 'block';
     		})
     		.delay(o.delay)
     		.start();
			
			return this;
		},

		/**
		 * Hides menu
		 * @return {null}
		 */
		hide: function() {
			var menu = this._getMenuHtml(),
				that = this;	
				
			if(menu.style.opacity <= 0)
				return this;		

     		this._tweenO.stop().to({'opacity': '0'}, 200)
     		.onUpdate(function() {
     			menu.style.opacity = that._style.opacity;
     		})
     		.onComplete(function() {
     			menu.style.display = 'none';
     		})
     		.start();

     		return this;
		}
	};

	ContextMenu.prototype.constructor = ContextMenu;

	fn.extend({
		contextMenu: util.bind(ContextMenu, this)
	})

	// Add context menu functionality to item
	context.on('itemSelected', function(item) {
		
		var menu = item.ref.menu;
		if(menu instanceof ContextMenu) {
			var bbox = item.handle.getBBox();
			menu._sender = item.ref;
			menu.show(bbox.x + bbox.width / 2, bbox.y + bbox.height / 2, item.ref);	// center of item handler
		}
	});

	context.on('mouseClick panelDragStart itemDragStart', function(e) {
		for(var i = 0, len = menusRef.length; i < len; i++) {
			menusRef[i].hide();
		}
	});
	
});